package com.hunter.lucene.util.annotation.parse;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import com.hunter.lucene.util.annotation.Undocument;
import com.hunter.lucene.util.annotation.Unfieldable;

/**
 * 
 * @author bastengao
 *
 */
public class UndocumentAnnotationParser {
	/**
	 * 解析在类上的与Undocument和Unfieldabel相关的注解信息，并返回。
	 * 
	 * @param documentAnnotationedClass
	 * @throw IllegalArguentException if clazz has not been presented by
	 *        Undocument annotation.
	 * @return
	 */
	public UndocumentAnnotationWrapper parse(Class<?> annotationedClass) {
		if (!annotationedClass.isAnnotationPresent(Undocument.class)) {
			throw new IllegalArgumentException(
					"the class has no \"Undocument\" annotation :"
							+ annotationedClass);
		}

		Undocument undocument = annotationedClass
				.getAnnotation(Undocument.class);

		UndocumentAnnotationWrapper wrapper = new UndocumentAnnotationWrapper();

		wrapper.setAnnotation(undocument);
		wrapper.setAnnotationedClass(annotationedClass);

		try {
			wrapper
					.setUnfieldabelAnnotationWrappers(parseField(annotationedClass));
		} catch (IntrospectionException e) {
			e.printStackTrace();
		}

		wrapper.getUnfieldabelAnnotationWrappers().addAll(
				parseMethod(annotationedClass));

		return wrapper;
	}

	private List<UnfieldableAnnotationWrapper> parseField(Class<?> clazz)
			throws IntrospectionException {
		Map<String, PropertyDescriptor> properties = DocumentAnnotationParser
				.getPropertyMap(clazz);
		List<UnfieldableAnnotationWrapper> wrappers = new ArrayList<UnfieldableAnnotationWrapper>();
		Field[] fields = clazz.getDeclaredFields();

		for (Field field : fields) {
			if (field.isAnnotationPresent(Unfieldable.class)) {
				UnfieldableAnnotationWrapper wrapper = new UnfieldableAnnotationWrapper();
				Unfieldable unfieldable = field
						.getAnnotation(Unfieldable.class);
				wrapper.setAnnotation(unfieldable);

				Class<?> fieldClass = field.getType();
				wrapper.setAnnotationedClass(fieldClass);

				PropertyDescriptor property = properties.get(field.getName());
				Method writeMethod = property.getWriteMethod();

				if (writeMethod == null) {
					throw new IllegalStateException("field : "
							+ field.getName()
							+ " has not a writeMethod(setter method).");

				} else {
					wrapper.setWriteMethod(writeMethod);
				}

				wrappers.add(wrapper);
			}

		}
		return wrappers;
	}

	private List<UnfieldableAnnotationWrapper> parseMethod(Class<?> clazz) {
		List<UnfieldableAnnotationWrapper> wrappers = new ArrayList<UnfieldableAnnotationWrapper>();
		Method[] methods = clazz.getDeclaredMethods();
		for (Method method : methods) {
			if (method.isAnnotationPresent(Unfieldable.class)) {

				UnfieldableAnnotationWrapper wrapper = new UnfieldableAnnotationWrapper();

				wrapper.setAnnotation(method.getAnnotation(Unfieldable.class));
				wrapper.setAnnotationedClass(method.getParameterTypes()[0]);
				wrapper.setWriteMethod(method);

				wrappers.add(wrapper);

			}
		}
		return wrappers;
	}
}
